/*************************************************************
 * File: SpringJaxRsApplication.java
 * Copyright (c) 2012
 * Author: Shaobin.Software@gmail.com Shaobin_Software@163.com 
 * Date: Sep 8, 2012
 * Description: 
 ************************************************************/

package snow.core.web.restlet;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.core.Application;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.restlet.Component;
import org.restlet.ext.jaxrs.InstantiateException;
import org.restlet.ext.jaxrs.JaxRsApplication;
import org.restlet.ext.jaxrs.JaxRsRestlet;
import org.restlet.ext.jaxrs.ObjectFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;


/**
 * 支持Spring注入的JaxRsApplication
 * 
 * @author Shaobin.Software@gmail.com Shaobin_Software@163.com
 * @version 1.0
 */
public class SpringJaxRsApplication extends JaxRsApplication implements BeanFactoryAware, InitializingBean {

	private final Log log = LogFactory.getLog(getClass());

	private Component component;
	private BeanFactory beanFactory;

	public void afterPropertiesSet() throws Exception {
		log.info("Start Execute afterPropertiesSet Method...");
		try {
			log.info("Execute Super's method setContext...");
			super.setContext(component.getContext().createChildContext());
			if (beanFactory instanceof ListableBeanFactory) {
				final ListableBeanFactory factory = (ListableBeanFactory) beanFactory;
				log.info("Execute Super's method add...");
				super.add(new Application() {

					public Set<Class<?>> getClasses() {
						log.info("Execute getClasses method...");
						// 此处如果加了代理会抛出异常
						Map<String, ResourceSupport> map = factory.getBeansOfType(ResourceSupport.class);
						final Collection<ResourceSupport> collection = map.values();
						final Set<Class<?>> classes = new HashSet<Class<?>>(collection.size());
						for (final Object object : collection) {
							if (object != null) {
								final Class<?> targetClass = AopUtils.getTargetClass(object);
								log.debug("Add The Class Of Resource -> " + targetClass);
								classes.add(targetClass);
							}
						}
						return classes;
					}
				});
			}
			component.getDefaultHost().attach(this);
			this.initProvider();
		} finally {
			log.info("Close Execute afterPropertiesSet Method...");
		}
	}

	public BeanFactory getBeanFactory() {
		return beanFactory;
	}

	public Component getComponent() {
		return component;
	}

	private JsonExtensionProvider getJsonExtensionProvider() {
		return beanFactory.getBean(JsonExtensionProvider.class);
	}

	/** 
	 * 
	 */
	protected void initProvider() {
		JaxRsRestlet restlet = super.getJaxRsRestlet();
		restlet.addSingleton(getJsonExtensionProvider());
	}

	/**
	 * @param beanFactory
	 * @throws BeansException
	 * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
	 */
	public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
		setObjectFactory(new ObjectFactory() {
			/**
			 * @param <T>
			 * @param jaxRsClass
			 * @return
			 * @throws InstantiateException
			 * @see org.restlet.ext.jaxrs.ObjectFactory#getInstance(java.lang.Class)
			 */
			public <T> T getInstance(Class<T> jaxRsClass) throws InstantiateException {
				return beanFactory.getBean(jaxRsClass);
			}
		});
	}

	public void setComponent(Component component) {
		this.component = component;
	}
}
